#ifndef ANIME_H
#define ANIME_H

#include <vector>
#include <string>
#include <typeinfo>
#include <algorithm>

#include <QObject>
#include <QThread>
#include <QTime>
#include <QMutex>

using namespace std;

namespace anime
{
    /* -------------------- Observable -------------------------------------- */
    template <class Observer> class Observable
    {
    public:
        Observable(){ observers = new vector<Observer*>(); }
        virtual ~Observable() { observers->clear(); delete observers; }

        virtual void AddObserver(Observer* o){ observers->push_back(o); }

        virtual void RemoveObserver(Observer* o){
            typename vector<Observer*>::iterator i = find(observers->begin(), observers->end(), o);
            if (i != observers->end()) observers->erase(i);
        }

        virtual void ClearObservers(){ observers->clear(); }

        virtual int GetObserversCount(){ return observers->size(); }

    protected:
        vector<Observer*>* observers;
    };
    /* ----------------------------------------------------------------------- */

    /* ------------------ Typeable ------------------------------------------- */
    class Typeable
    {
    public:
        virtual ~Typeable(){}
        virtual string GetType() = 0;
    };
    /* ----------------------------------------------------------------------- */

    /* ----------------- PropertyGetter and SimplePropertyGetter ------------- */
    template <class T> class PropertyGetter
    {
    public:
        virtual T operator()(void) = 0;
    };

    template <class T> class SimplePropertyGetter: public PropertyGetter<T>
    {
    public:
        SimplePropertyGetter(T* _t):t(_t){}
        virtual T operator()(void){ return *t; }

    private:
        T* t;
    };
    /* ----------------------------------------------------------------------- */

    /* --------------------- PropertySetter and SimplePropertySetter --------- */
    template <class T> class PropertySetter
    {
    public:
        virtual void operator()(const T& val) = 0;
    };

    template <class T> class SimplePropertySetter: public PropertySetter<T>
    {
    public:
        SimplePropertySetter(T* _t):t(_t){}
        virtual void operator()(const T& val){ *t = val; }

    private:
        T* t;
    };
    /* ----------------------------------------------------------------------- */

    /* ------------- Property Observer --------------------------------------- */
    template <class T> class PropertyObserver
    {
    public:
        virtual void PropertyChanged(const T& newVal) = 0;
        virtual void MaxChanged(const T& val) = 0;
        virtual void MinChanged(const T& val) = 0;
        virtual bool IsObserving() = 0;
    };
    /* ----------------------------------------------------------------------- */

    /* ------------- Property and Simple Property ---------------------------- */
    class PropertyBase: public Typeable
    {
    public:
        virtual string GetName() = 0;
    };

    template <class T> class Property: public PropertyBase, public Observable<PropertyObserver <T> >
    {
    public:
        Property(const string& _name, PropertyGetter<T>* _getter,
                 PropertySetter<T>* _setter, bool* _invalideFlag = 0)
        {
            name = _name;
            getter = _getter; setter = _setter; invalideFlag = _invalideFlag;
        }

        string GetType() { return typeid(T).name(); }

        void operator()(const T& val){
            (*setter)(val);
            if(invalideFlag) *invalideFlag = true;

            T t = (*getter)();
            for(typename vector< PropertyObserver<T>* >::iterator i=this->observers->begin();
                i!=this->observers->end(); ++i)
            {
                if((*i)->IsObserving())
                    (*i)->PropertyChanged(t);
            }
        }

        T operator()(void)
        {
            return (*getter)();
        }

        string GetName() { return name; }

    protected:
        PropertyGetter<T>* getter;
        PropertySetter<T>* setter;
        bool* invalideFlag;
        string name;
    };

    template <class T> class SimpleProperty: public Property<T>
    {
    public:
        SimpleProperty(const string& name, T* t, bool* invalideFlag = 0)
            :Property<T>(name, 0, 0, invalideFlag)
        {
            this->getter = new SimplePropertyGetter<T>(t);
            this->setter = new SimplePropertySetter<T>(t);
        }

        virtual ~SimpleProperty(){ delete this->getter; delete this->setter; }
    };
    /* ------------------------------------------------------------------------ */

    /* ------- Interpolator --------------------------------------------------- */
    template <class A, class B> class Interpolator: public PropertyObserver<A>, public Typeable
    {
    public:
        Interpolator():interpolated(0), isObserving(true){}

        // PropertyObserver<A> implementation
        void PropertyChanged(const A& a)
        {
            if(interpolated != 0)
                (*interpolated)((*this)(a));
        }
        virtual bool IsObserving(){ return isObserving; }
        virtual void MaxChanged(const A& val){};
        virtual void MinChanged(const A& val){};

        // the interpolation method
        virtual B operator()(const A& a) = 0;

        virtual void Enable(){ isObserving = true; }
        virtual void Disable(){ isObserving = false; }
        virtual void SetEnabled(bool set){ isObserving = set; }

        string GetType()
        {
            string aType = typeid(A).name();
            string bType = typeid(B).name();
            return aType + bType;
        }

        void AttachTo(Property<B>* p){ interpolated = p; }

    private:
        Property<B>* interpolated;
        bool isObserving;
    };

    template <class T> class OneTypeInterpolator: public Interpolator<T, T>{};
    /* ------------------------------------------------------------------------ */

    /* ------ base normalized interpolators [0, 1] -> [0, 1] ------------------ */
    // ScalarType is a floating point type (float or double)
    template <class ScalarType> class NormalizedInterpolator
        :public OneTypeInterpolator<ScalarType>{};

    // linear normalized interpolator: f(x) = x
    template <class ScalarType> class Linear_NI
        :public NormalizedInterpolator<ScalarType>
    {
    public:
        virtual ScalarType operator()(const ScalarType& a){ return a; }
    };

    // power normalized interpolator: f(x) = x^t
    template <class ScalarType> class Power_NI
        :public NormalizedInterpolator<ScalarType>
    {
    public:
        Power_NI(ScalarType _exp):exp(_exp){}
        virtual ~Power_NI(){}

        virtual ScalarType operator()(const ScalarType& n){ return (ScalarType)(pow(n, exp)); }

    private:
        ScalarType exp;
    };

    // constant interpolator: f(x) = k
    template <class ScalarType> class Constant_NI
        :public NormalizedInterpolator<ScalarType>
    {
    public:
        Constant_NI(ScalarType _v):v(_v){}
        virtual ~Constant_NI(){}

        virtual ScalarType operator()(const ScalarType& n){ return v; }

    private:
        ScalarType v;
    };
    /* ------------------------------------------------------------------------ */

    /* ------------ composite normalized interpolators ------------------------ */
    // g(x, f) = 1 - f(x)
    template <class ScalarType> class OneMinus_NI
        :public NormalizedInterpolator<ScalarType>
    {
    public:
        OneMinus_NI(NormalizedInterpolator<ScalarType>* _n_is, bool _deleteIt = false)
            :n_is(_n_is), deleteIt(_deleteIt){}

        virtual ~OneMinus_NI()
        {
            if(deleteIt) delete n_is;
        }

        virtual ScalarType operator()(const ScalarType& n){return (ScalarType)(1 - (*n_is)(n));}

    private:
        NormalizedInterpolator<ScalarType>* n_is;
        bool deleteIt;
    };

    // g(x, [(f1, x1), (f2, x2), ..., fn(xn)]) =
    //      if(x in [x(i-1), x(i)]) then fi((x-x(i-1))/(x(i)-x(i-1)))
    template <class ScalarType> class Composite_NI
        :public NormalizedInterpolator<ScalarType>
    {
    public:
        Composite_NI(vector< NormalizedInterpolator<ScalarType>* >& _strategies,
                     vector< ScalarType >* _thresholds)
        {
            strategies = new vector< NormalizedInterpolator<ScalarType>* >();
            thresholds = new vector< ScalarType >();
            *strategies = _strategies;
            *thresholds = _thresholds;
        }

        virtual ~Composite_NI()
        {
            thresholds->clear();
            delete thresholds;
            strategies->clear();
            delete strategies;
        }

        virtual ScalarType operator()(const ScalarType& n)
        {
            bool found = false;
            unsigned int i = 0;

            while(!found && i<thresholds->size() - 1)
            {
                found = (n <= (*thresholds)[i+1]);
                i++;
            }

            unsigned int index = found? i-- : i;
            ScalarType min = (*thresholds)[index];
            ScalarType max = found? (*thresholds)[index+1] : 1;

            ScalarType n2 = (n - min)/(max - min);
            return (*((*strategies)[index]))(n2);
        }

    private:
        vector< NormalizedInterpolator<ScalarType>* >* strategies;
        vector< ScalarType >* thresholds;
    };
    /* ------------------------------------------------------------------------ */

    /* ----------- scalar interpolator ---------------------------------------- */
    template <class A, class B, class NormType = float>
        class ScalarInterpolator: public Interpolator<A, B>
    {
    public:
        ScalarInterpolator(NormalizedInterpolator<NormType>* _n_is,
                           const A& _min_x, const A& _max_x, const B& _min_y, const B& _max_y,
                           bool deleteNormalizedStrategy = false)
        {
            n_is = _n_is;
            min_x = _min_x; range_x = _max_x - _min_x; max_x = _max_x;
            min_y = _min_y; range_y = _max_y - _min_y;
            deleteStrategy = deleteNormalizedStrategy;
            intName = typeid(int).name();
        }

        virtual ~ScalarInterpolator()
        {
            if (deleteStrategy)
                delete n_is;
        }

        virtual B operator()(const A& a)
        {
            A a1 = a;
            if (a1 <= min_x) a1 = min_x;
            if (a1 >= max_x) a1 = max_x;
            NormType nres = (*n_is)((a1 - min_x)/(NormType)range_x);
            NormType res = (nres * range_y) + min_y;
            if(typeid(B).name() == intName)
            {
                NormType decimalPart = res - (int)res;
                return ((decimalPart > NormType(0.5))? (int)(res + 1): (int)res);
            }
            return (B)(res);
        }

        virtual void MaxChanged(const A &val)
        {
            SetMaxX(val);
        }

        virtual void MinChanged(const A &val)
        {
            SetMinX(val);
        }

        void SetMaxX(const A& a)
        {
            max_x = a;
            range_x = max_x - min_x;
        }

        void SetMinX(const A& a)
        {
            min_x = a;
            range_x = max_x - min_x;
        }

        void SetMinY(B b)
        {
            range_y += (min_y - b);
            min_y = b;
        }

        void SetMaxY(B b)
        {
            range_y = b - min_y;
        }

    protected:
        NormalizedInterpolator<NormType>* n_is;
        A min_x, range_x, max_x;
        B min_y, range_y;
        string intName;

    private:
        bool deleteStrategy;
    };
    /* ------------------------------------------------------------------------ */

    /* ---------------- other interpolators ----------------------------------- */
    class SimpleBoolInterpolator: public OneTypeInterpolator<bool>
    {
    public:
        SimpleBoolInterpolator(bool _flip = false) : flip(_flip){}
        virtual ~SimpleBoolInterpolator(){}

        bool operator()(const bool& b)
        {
            if (flip) return !b;
            return b;
        }

    private:
        bool flip;
    };
    /* ------------------------------------------------------------------------ */

    /* ---------------- Interpolable ------------------------------------------ */
    class Interpolable: public QObject
    {
        Q_OBJECT

    public:
        typedef vector<PropertyBase*> Properties;

        Interpolable() { properties = new Properties(); }

        virtual ~Interpolable()
        {
            for (Properties::iterator i=properties->begin(); i!=properties->end(); ++i)
            {
                delete (*i);
            }
            properties->clear();
            delete properties;
        }

        template <class T>
                static Property<T>* RegisterProperty(Interpolable* owner, const string& pName, T& t,
                                                     bool* invalideFlag = 0)
        {
            SimpleProperty<T>* p = new SimpleProperty<T>(pName, &t, invalideFlag);
            owner->properties->push_back(p);
            return p;
        }

        template <class T>
                static Property<T>* RegisterProperty(Interpolable *owner, const string &pName,
                                                     PropertyGetter<T>* getter, PropertySetter<T>* setter, bool* invalideFlag = 0)
        {
            Property<T>* p = new Property<T>(pName, getter, setter, invalideFlag);
            owner->properties->push_back(p);
            return p;
        }

        template <class T>
                static Property<T>* GetProperty(Interpolable* owner, const string& pName)
        {
            bool found = false;
            Properties::iterator i = owner->properties->begin();

            while(!found && i!=owner->properties->end())
            {
                found = (pName == (*i)->GetName());
                ++i;
            }
            if (!found) return 0;
            --i;
            return (Property<T>*)(*i);
        }

        static void GetProperties(Interpolable* owner, vector< pair<string, string> >& toFill)
        {
            toFill.clear();
            for(Properties::iterator i=owner->properties->begin(); i!=owner->properties->end(); ++i)
            {
                toFill.push_back(pair<string, string>((*i)->GetName(), (*i)->GetType()));
            }
        }

    protected:
        Properties* properties;
    };
    /* ------------------------------------------------------------------------ */

    /* ------------------------- Drawable ------------------------------------- */
    class Drawable
    {
    public:
        Drawable(){invalidated = false;}
        virtual ~Drawable(){}

        virtual void Draw() = 0;

    protected:
        bool invalidated;
    };
    /* ------------------------------------------------------------------------ */

    /* -------------- InterpolationGraph -------------------------------------- */
    class InterpolationGraph
    {
    public:
        typedef pair<Typeable*, Typeable*> PropInt; // (interpolator, observed_property)
        typedef vector<PropInt> Interpolators;

        InterpolationGraph(){}

        virtual ~InterpolationGraph()
        {
            for(Interpolators::iterator i=interpolators.begin(); i!= interpolators.end(); ++i)
            {
                delete (*i).first;
            }
        }

        template <class A, class B>
                static void AddInterpolator(InterpolationGraph& g, Property<A>* observed,
                                            Interpolator<A, B>* i, Property<B>* interpolated = 0)
        {
            i->AttachTo(interpolated);
            observed->AddObserver(i);
            g.interpolators.push_back(PropInt(i, observed));
        }

        template <class A, class B>
                static void RemoveInterpolator(InterpolationGraph& g, Interpolator<A, B>* interpolator)
        {
            bool found = false;
            Interpolators::iterator i = g.interpolators.begin();

            while(!found && i!=g.interpolators.end())
            {
                found = ((*i).first == interpolator);
                ++i;
            }

            if(found)
            {
                --i;
                Property<A>* observed = (Property<A>*)((*i).second);
                Interpolator<A, B>* interpolator = (Interpolator<A, B>*)((*i).first);
                g.interpolators.erase(i);
                observed->RemoveObserver(interpolator);
                delete interpolator;
            }
        }

    private:
        Interpolators interpolators;
    };
    /* ------------------------------------------------------------------------ */

    /* ---------------- Animation --------------------------------------------- */
    class Time: public SimpleProperty<int>
    {
    public:
        Time(int* t):SimpleProperty<int>("time", t){}
        virtual ~Time(){}

        void SetMax(int max)
        {
            for(vector< PropertyObserver<int>* >::iterator i=observers->begin();
                i!=observers->end(); ++i)
            {
                (*i)->MaxChanged(max);
            }
        }
    };

    class Animation;

    class AnimationObserver
    {
    public:
        virtual void OnPause(Animation* a) = 0;
        virtual void OnStop(Animation* a) = 0;
        virtual void OnEnd(Animation* a) = 0;
        virtual void OnPlay(Animation* a) = 0;
    };

    class Animation: public Observable< AnimationObserver >
    {
    public:
        void Play()
        {
            running = true;
            Trigger(EVENT_PLAY);
        }

        void Stop()
        {
            running = false;
            reversed = false;
            currentTime = 0;
            time(currentTime);
            Trigger(EVENT_STOP);
        }

        void Pause()
        {
            running = false;
            Trigger(EVENT_PAUSE);
        }

        bool Reverse(bool set)
        {
            if(running) return false;
            reversed = set;
            return true;
        }

        void Reverse()
        {
            reversed = !reversed;
        }

        bool IsReversed()
        {
            return reversed;
        }

        virtual void Tick()
        {
            if(!running) return;
            if(reversed? (currentTime < 0) : (currentTime > duration))
            {
                running = false;
                Trigger(EVENT_END);
            } else
            {
                time(currentTime);
                if(reversed)
                {
                    currentTime -= step;
                } else
                {
                    currentTime += step;
                }
            }
        }

        Property<int>* GetTime(){ return &time; }
        int GetDuration() { return duration; }

        bool SetCurrentTime(int newTime)
        {
            if(running) return false;
            if(newTime >= 0 && newTime <= duration)
            {
                currentTime = newTime;
                return true;
            }
            return false;
        }

        bool SetDuration(int newDuration)
        {
            if(running) return false;
            duration = newDuration;
            time.SetMax(newDuration);
            return true;
        }

        bool IsRunning() { return running; }

        Animation(int fps, int length):running(false), reversed(false), duration(length),
            currentTime(0), step(1000/fps), time(&currentTime)
        {
            time.SetMax(duration);
        }

        virtual ~Animation(){}

    protected:
        bool running;
        bool reversed;
        int duration;
        int currentTime;
        int step;
        Time time;

        void Trigger(int event)
        {
            for(vector<AnimationObserver*>::iterator i=observers->begin(); i!=observers->end(); ++i)
            {
                switch(event)
                {
                case EVENT_PLAY:
                    (*i)->OnPlay(this);
                    break;
                case EVENT_END:
                    (*i)->OnEnd(this);
                    break;
                case EVENT_STOP:
                    (*i)->OnStop(this);
                    break;
                case EVENT_PAUSE:
                    (*i)->OnPause(this);
                    break;
                }
            }
        }

        const static int EVENT_PLAY = 0;
        const static int EVENT_END = 1;
        const static int EVENT_PAUSE = 2;
        const static int EVENT_STOP = 3;
    };
    /* ------------------------------------------------------------------------ */
}

#endif // ANIME_H
